#pragma once
#include <cstddef>
#include <initializer_list>
#include <stdint.h>
#include <stdexcept>
#define DEG2RAD(theta) (0.01745329251994329 * (theta))
namespace math
{    
    	// 前置声明
	template <typename T, std::size_t Rows, std::size_t Cols>
	class Matrix;

    template <typename Derived, typename T, std::size_t R, std::size_t C>
    class MatrixExpr
    {

    public:
        Derived const &derived() const
        {

            return static_cast<Derived const &>(*this);
        }

        T operator()(std::size_t r, std::size_t c) const
        {

            return derived()(r, c);
        }
    };

    template <typename LHS, typename RHS, typename T, std::size_t R, std::size_t C>
    class AddExpr : public MatrixExpr<AddExpr<LHS, RHS, T, R, C>, T, R, C>
    {
        LHS const &lhs;
        RHS const &rhs;

    public:
        AddExpr(LHS const &l, RHS const &r) : lhs(l), rhs(r) {}
        T operator()(std::size_t row, std::size_t col) const
        {
            return lhs(row, col) + rhs(row, col);
        }
    };

    template <typename LHS, typename RHS, typename T, std::size_t R, std::size_t C>
    AddExpr<LHS, RHS, T, R, C> operator+(
        const MatrixExpr<LHS, T, R, C> &lhs,
        const MatrixExpr<RHS, T, R, C> &rhs)
    {
        return AddExpr<LHS, RHS, T, R, C>(lhs.derived(), rhs.derived());
    }

    template <typename LHS, typename RHS, typename T,
              std::size_t R, std::size_t M, std::size_t C>
    class MulExpr : public MatrixExpr<MulExpr<LHS, RHS, T, R, M, C>, T, R, C>
    {
        LHS const &lhs;
        RHS const &rhs;

    public:
        MulExpr(LHS const &l, RHS const &r) : lhs(l), rhs(r) {}

        T operator()(std::size_t row, std::size_t col) const
        {
            T sum = 0;
            for (std::size_t k = 0; k < M; ++k)
            {
                sum += lhs(row, k) * rhs(k, col);
            }
            return sum;
        }
    };

    // 矩阵乘法运算符重载
    template <typename LHS, typename RHS, typename T,
              std::size_t R, std::size_t M, std::size_t C>
    MulExpr<LHS, RHS, T, R, M, C> operator*(
        const MatrixExpr<LHS, T, R, M> &lhs,
        const MatrixExpr<RHS, T, M, C> &rhs)
    {
        return MulExpr<LHS, RHS, T, R, M, C>(lhs.derived(), rhs.derived());
    }

    template <typename HS, typename T, std::size_t R, std::size_t C>
    class TransposeExpr : public MatrixExpr<TransposeExpr<HS, T, R, C>, T, R, C>
    {
        HS const &hs;

    public:
        TransposeExpr(HS const &h) : hs(h) {}
        T operator()(std::size_t row, std::size_t col) const
        {
            return hs(col, row);
        }
    };

     template <typename Expr,typename T, std::size_t Rows, std::size_t Cols>
	inline Matrix<T,Rows,Cols> from_expr(const MatrixExpr<Expr, T, Rows, Cols> &expr)
	{
		Matrix<T,Rows,Cols> result;
		for (size_t i = 0; i < Rows; ++i)
		{  
            for(size_t j = 0; j < Cols; ++j){
                result(i,j) = expr(i,j);
            }
		}
		return result;
	}
    // LU求大矩阵运算，后续有需要再来实现该表达式
    // classSolveExpr
     template <typename T>
    Matrix<T, 4, 4> scale(Matrix<T, 4, 4> &src, T x, T y, T z)
    {
        Matrix<T, 4, 4> scaleMatrix = {
            {x, 0, 0, 0},
            {0, y, 0, 0},
            {0, 0, z, 0},
            {0, 0, 0, 1}};
        return from_expr(src * scaleMatrix);
    }

    template <typename T>
    Matrix<T, 4, 4> translate(Matrix<T, 4, 4> &src, T x, T y, T z)
    {
        Matrix<T, 4, 4> translateMatrix = {
            {1, 0, 0, x},
            {0, 1, 0, y},
            {0, 0, 1, z},
            {0, 0, 0, 1}};
        return from_expr(src * translateMatrix);
    }

    template <typename T>
    Matrix<T, 4, 4> translate(Matrix<T, 4, 4> &src, const Vector<T, 3> &v)
    {
        return translate(src, v[0], v[1], v[2]);
    }

    // 绕任意轴旋转
    template <typename T>
    Matrix<T, 4, 4> rotate(const Matrix<T, 4, 4> &src, float angle, const Vector<T, 3> &v)
    {
        T const c = std::cos(angle);
        T const s = std::sin(angle);
        Vector<T, 3> axis = normalize(v);
        T x = axis[0];
        T y = axis[1];
        T z = axis[2];
        Matrix<T, 4, 4> rotateMatrix = {
            {x * x * (1 - c) + c, x * y * (1 - c) - z * s, x * z * (1 - c) + y * s, 0},
            {x * y * (1 - c) + z * s, y * y * (1 - c) + c, y * z * (1 - c) - z * s, 0},
            {x * z * (1 - c) - y * s, x * z * (1 - c) + x * s, z * z * (1 - c) + c, 0},
            {0, 0, 0, 1}}; 
        return from_expr(src * rotateMatrix);
    }

    // 正交投影矩阵
    template <typename T>
    Matrix<T, 4, 4> orthographic(T left, T right, T bottom, T top, T near, T far)
    {
        Matrix<T, 4, 4> result = {
            {2 / (right - left), 0, 0, -(right + left / right - left)},
            {0, 2 / (top - bottom), 0, -(top + bottom / top - bottom)},
            {0, 0, 2 / (far - near), -(near + far / far - near)},
            {0, 0, 0, 1},
        };
        return result;
    }

    // 透视投影矩阵
    template <typename T>
    Matrix<T, 4, 4> perspective(T fovy, T aspect, T n, T f)
    {
        T const tanHalfFovy = std::tan(DEG2RAD(fovy / static_cast<T>(2)));
        Matrix<T, 4, 4> result = {
            {static_cast<T>(1) / (aspect * tanHalfFovy), 0, 0, 0},
            {0, static_cast<T>(1) / (tanHalfFovy), 0, 0},
            {0, 0, -((f + n) / (f - n)), -(static_cast<T>(2) * f * n / (f - n))},
            {0, 0, -1, 0},
        };
        return result;
    }

    // 屏幕变换矩阵
    template <typename T>
    Matrix<T, 4, 4> screenMatrix(const uint32_t &width, const uint32_t &height)
    {

        Matrix<T, 4, 4> result = {
            {(float)width / 2, 0, 0, (float)width / 2},
            {0, (float)height / 2, 0, (float)height / 2},
            {0, 0, 0.5, 0.5},
            {0, 0, 0, 1},
        };
        return result;
    }

}